package org.protege.editor.owl.ui.renderer;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Author: Piotr Nowara<br>
* piotrnowara@gmail.com<br>
* code made available under Mozilla Public License (http://www.mozilla.org/MPL/MPL-1.1.html)<br>
* copyright 2012, Piotr Nowara<br>
*/
public class EventRendering 
{
	private static final Pattern pattern = Pattern.compile(" [(]{0,}[']{0,1}(occurs|follows|preceeds|występuje jednocześnie|poprzedza|następuje|występuje)[a-zA-Z0-9_\\s'żźćńąśłęó]{0,}([(]){0,1}");
	private static final Pattern oBracketPattern = Pattern.compile("\\(");

	public static String transformRendering(String expr)
	{
		String exprBackup = expr;
		int level = 0;
		ArrayList<Integer> levelChangesIndex = new ArrayList<Integer>();
		
		try 
		{
			expr = expr.replaceAll("\t", " ");
			expr = expr.replaceAll("\n", " ");
			expr = expr.replaceAll("\r", " ");
			expr = expr.replaceAll(" {2,}", " ");
			expr = removeUnneededBrackets(expr);
			int offset = 0;
			while(true)
			{
				Matcher m = pattern.matcher(expr);
				int charsAdded = 0;
				boolean noBrackets = false;
				if(m.find(offset))
				{
					int sectionBegining = m.start()+1;//+1 beacause of regex matches trailing space
					for(int i=levelChangesIndex.size()-1;i>=0;i--)
					{
						if(levelChangesIndex.get(i)!=null && levelChangesIndex.get(i).intValue() < sectionBegining)
						{
							levelChangesIndex.remove(i);
							levelChangesIndex.add(null);
							level--;
							if(level<0)
								throw new Exception("Below 0");
						}
					}
					int sectionEnd = 0;
					if(m.group(2) != null)
					{
						sectionEnd = determineBracketRange(expr, (m.end()-1));
						if(sectionEnd == -1)
							throw new Exception("Missing bracket!");
						levelChangesIndex.add(sectionEnd);
					}
					else//if there's no opening bracket after object property (for example where there's named class instead of anonymous one like: "(...) and preceeds some Drug_4_issuance"
					{
						sectionEnd = m.end()+1;
						levelChangesIndex.add(m.end()+1);
						noBrackets = true;
					}
					level++;
					StringBuffer sb = new StringBuffer();
					for(int ii=0;ii<level;ii++)
						sb.append("\t");
					String replacement = "\n".concat(sb.toString());
					expr = expr.substring(0,sectionBegining).concat(replacement).concat(expr.substring(sectionBegining));
					charsAdded = replacement.length();
					
					if(!noBrackets)
					{
						boolean newlineAlreadyExists = false;
						int breakLine = sectionEnd;
						while(true)
						{
							if(breakLine+charsAdded == expr.length())
								break;
							else if(expr.charAt(breakLine+charsAdded) == '\n')
							{
								newlineAlreadyExists = true;
								break;
							}
							else if(expr.charAt(breakLine+charsAdded) != ')')
								break;
							else
								breakLine++;
						}
						if(!newlineAlreadyExists)
						{
							sb = new StringBuffer();
							for(int ii=0;ii<level-1;ii++)
								sb.append("\t");
							String replacement2 = "\n".concat(sb.toString());
							expr = expr.substring(0,breakLine+charsAdded).concat(replacement2).concat(expr.substring(breakLine+charsAdded));
							charsAdded = charsAdded + replacement2.length();
						}
					}
					
					offset = m.end() + charsAdded;
				}
				else
					break;
			}
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
			return exprBackup;
		}
		expr = expr.replaceAll("\n and \n", " and \n");
		expr = expr.replaceAll("\n or \n", " or \n");
		expr = expr.replaceAll("\n i \n", " i \n");
		expr = expr.replaceAll("\n lub \n", " lub \n");
		expr = expr.replaceAll("\t and ", "\tand ");
		expr = expr.replaceAll("\t or ", "\tor ");
		expr = expr.replaceAll("\t i ", "\ti ");
		expr = expr.replaceAll("\t lub ", "\tlub ");
		expr = expr.replaceAll("\n and ", "\nand ");
		expr = expr.replaceAll("\n or ", "\nor ");
		expr = expr.replaceAll("\n i ", "\ni ");
		expr = expr.replaceAll("\n lub ", "\nlub ");
		expr = expr.replaceAll("\n\n", "\n");
		expr = expr.trim();
		return expr;
	}


    
	/*
	 * Returns -1 if the bracket is not closed in the given piece of text
	 */
	public static int determineBracketRange(String text, int startIndex) throws Exception 
	{
		if(text == null)
			throw new NullPointerException();
		
		if(text.charAt(startIndex) != '(')
			throw new Exception("Character '"+text.charAt(startIndex)+"' encountered at specified start index is not a bracket!");
		
		int i = -1, range = -1, level = 0;
		for(char ch:text.substring(startIndex).toCharArray()) 
		{
			i++;
			if(ch == '(')
				level++;
			else if(ch == ')')
				level--;
			
			if(level == 0)
			{
				range = i;
				break;
			}
		}
	
		if(range > -1)
			return range + startIndex;
		else
			return range;
	}
	
	/*
	 * 1. jeśli w nawiasie jest OR lub AND, to zostaje 
	 * 2. jeśli bezpośrendo po nazwie relacj z wyrażeniem SOME lub MIN, to zostaje 
	 * 3. jeśli po NOT, to zostaje
	 * 4. w przeciwnym razie nawias jest usuwany (jeśli po otwarciu nawiasu jest następny nawias, to najpierw rozpatrujemy ten zagnieżdżony).
	 */
	public static String removeUnneededBrackets(String expression) throws Exception 
	{
		boolean isChanged = false;
		do
		{
			Matcher m = oBracketPattern.matcher(expression);
			isChanged = false;
			while(m.find())
			{
				int beginIndex = m.start();
				if(expression.charAt(beginIndex + 1) == '(')
				{
					continue;
				}
				else
				{
					if(beginIndex >= 4)
					{
						if(expression.substring(beginIndex - 4, beginIndex).equalsIgnoreCase("not "))//3
							continue;
						if(expression.substring(beginIndex - 4, beginIndex).equalsIgnoreCase("nie "))//3
							continue;
					}
					if(beginIndex >= 5)
					{
						if(expression.substring(beginIndex - 5, beginIndex).equalsIgnoreCase("some "))//2
							continue;
					}
					if(beginIndex >= 6)
					{
						boolean isMinExpr = false;
						int spaceCount = 0;
						for(int i=beginIndex-1;i>=0;i--)
						{
							if(expression.charAt(i) == ' ')
								spaceCount++;
							else if(expression.charAt(i) == ')')
							{
								isMinExpr = false;
								break;
							}
							
							if(spaceCount == 3)
							{
								if(expression.toLowerCase().startsWith("min", i+1))
									isMinExpr = true;
								break;
							}
						}
						if(isMinExpr)//2
							continue;
					}
						
					int endIndex = determineBracketRange(expression, beginIndex);
					if(endIndex <= expression.length() - 1)
					{
						String frgm = expression.substring(beginIndex+1);
						String relation = "";
						if(expression.substring(beginIndex+1).indexOf(" ") > -1)
							relation = frgm.substring(0, expression.substring(beginIndex+1).indexOf(" ")).replaceAll("'", "");
						int indexOfNextBracket = frgm.indexOf(")");
				    	if(expression.substring(beginIndex+1, endIndex).toLowerCase().contains(" or ") || expression.substring(beginIndex+1, endIndex).toLowerCase().contains(" lub "))//1
							continue;
				    	else if(!checkIfTemporalRelation(relation) && (frgm.substring(0, indexOfNextBracket).toLowerCase().contains(" and ") || frgm.substring(0, indexOfNextBracket).toLowerCase().contains(" i ")))//1
				    		continue;
//				    	else if(expression.substring(beginIndex+1, endIndex).contains(") or occurs") || expression.substring(beginIndex+1, endIndex).contains(") or follows") || expression.substring(beginIndex+1, endIndex).contains(") or preceeds") || expression.substring(beginIndex+1, endIndex).contains(") lub occurs") || expression.substring(beginIndex+1, endIndex).contains(") lub 'występuje"))
//				    		continue;//to sprawdza się w owlToSql ale tu nie możemy wyciąć nawiasu zawierającego 'or'
					}

					expression = expression.substring(0, beginIndex).concat(expression.substring(beginIndex + 1, endIndex)).concat(expression.substring(endIndex + 1));
					isChanged = true;
					break;
					
				}
			}
		}
		while(isChanged);
		return expression;
	}
	
	private static boolean checkIfTemporalRelation(String objPropName)
	{
		return (objPropName.startsWith("occurs") || objPropName.startsWith("występuje") || objPropName.startsWith("follows") || objPropName.startsWith("preceeds") ||
				objPropName.startsWith("poprzedza") || objPropName.startsWith("następuje"));
	}
	
}
